package com.quectel.util.common;

import com.quectel.util.ex.common.ExcelException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * excel帮助类
 *
 * @author: CL
 * @email: louis.cao@quectel.com
 * @date: 2019-03-14 16:23:00
 */
public class POIUtils {
    /**
     * 导出excel
     *
     * @param response
     * @param title     sheet名称
     * @param headers   字段名称数组
     * @param dataList  数据
     * @param excelName excel下载的时候显示什么名字 后缀无需指定
     */
    public static void exportExcel(HttpServletResponse response, String title, String[] headers, List<String[]> dataList, String excelName) {
        if (!excelName.endsWith(".xlsx")) {
            //不是.xlsx结尾
            if (excelName.endsWith(".xls")) {
                excelName += "x";
            } else {
                excelName += ".xlsx";
            }
        }
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf8");
            String headerValue = "attachment;";
            headerValue += " filename=\"" + encodeURIComponent(excelName) + "\";";
            headerValue += " filename*=utf-8''" + encodeURIComponent(excelName);
            response.setHeader("Content-Disposition", headerValue);
            exportExcel(title, headers, dataList, response.getOutputStream());
        } catch (Exception e) {
            throw new ExcelException("Export excel failed", e);
        }
    }

    /**
     * <pre>
     * 符合 RFC 3986 标准的“百分号URL编码”
     * 在这个方法里，空格会被编码成%20，而不是+
     * 和浏览器的encodeURIComponent行为一致
     * </pre>
     *
     * @param value
     * @return
     */
    private static String encodeURIComponent(String value) {
        try {
            return URLEncoder.encode(value, "UTF-8").replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            throw new ExcelException("Encode uri failed", e);
        }
    }

    /**
     * 导出的文件应该是xlsx 支持大文件
     *
     * @param title
     * @param headers
     * @param dataList
     * @param out
     * @throws Exception
     */
    public static void exportExcel(String title, String[] headers, List<String[]> dataList, OutputStream out) throws Exception {
        Workbook wb = null;
        try {
            //注意实现类是SXSSFWorkbook
            wb = new SXSSFWorkbook(1000);
            Sheet sheet = wb.createSheet(title);
            sheet.setDefaultColumnWidth(20);
            sheet.setDefaultRowHeightInPoints(20);
            /*第二行*/
            Row headerRow = sheet.createRow(0);
            CellStyle headerCellStyle = wb.createCellStyle();
            headerCellStyle.setAlignment(HorizontalAlignment.LEFT);
            Font headerFont = wb.createFont();
            headerFont.setBold(true);
            headerCellStyle.setFont(headerFont);
            for (int i = 0; i < headers.length; i++) {
                Cell headerCell = headerRow.createCell(i);
                headerCell.setCellStyle(headerCellStyle);
                headerCell.setCellValue(headers[i]);
            }
            /*数据部分*/
            if (dataList != null && dataList.size() > 0) {
                for (int i = 0; i < dataList.size(); i++) {
                    String[] itemArray = dataList.get(i);
                    Row dataRow = sheet.createRow(i + 1);
                    int j = 0;
                    for (String item : itemArray) {
                        dataRow.createCell(j).setCellValue(item == null ? "" : item);
                        j++;
                    }
                }
            }
            /*写入数据*/
            wb.write(out);
        } catch (Exception e) {
            throw new ExcelException("Export excel throw exception", e);
        } finally {
            if (wb != null) {
                wb.close();
            }
        }
    }


    /**
     * 解析Excel文件到List
     *
     * @param excelFile 要解析的文件
     * @param startRow  真实数据的开始行 从0开始数
     * @param startCol  真实数据的开始列 从0开始数
     * @return List<Map>  map的key就是列数  从0开始计数
     */
    public static List<Map<Integer, String>> analysis(File excelFile, int startRow, int startCol) throws Exception {
        try (FileInputStream fis = new FileInputStream(excelFile)) {
            List<Map<Integer, String>> analysis = analysis(fis, startRow, startCol);
            return analysis;
        } catch (Exception e) {
            throw new ExcelException("Parse excel error", e);
        }
    }

    /**
     * 解析Excel文件到List
     *
     * @param excelFile  要解析的文件
     * @param sheetIndex 标签的下标
     * @param startRow   真实数据的开始行 从0开始数
     * @param startCol   真实数据的开始列 从0开始数
     * @return List<Map>  map的key就是列数  从0开始计数
     */
    public static List<Map<Integer, String>> analysis(File excelFile, int sheetIndex, int startRow, int startCol) throws Exception {
        try (FileInputStream fis = new FileInputStream(excelFile)) {
            List<Map<Integer, String>> analysis = analysis(fis, sheetIndex, startRow, startCol);
            return analysis;
        } catch (Exception e) {
            throw new ExcelException("Parse excel error", e);
        }
    }

    /**
     * 解析Excel文件到List xls类型
     *
     * @param is       要解析的流如果是servlet的输入流不需要手动关闭，其他自己手动创建的流需要自己关掉，在此方法中没有做关闭处理
     * @param startRow 真实数据的开始行 从0开始数
     * @param startCol 真实数据的开始列 从0开始数
     * @return List<Map>  map的key就是列数  从0开始计数
     */
    public static List<Map<Integer, String>> analysis(InputStream is, int startRow, int startCol) throws Exception {

        return analysis(is, 0, startRow, startCol);
    }

    /**
     * 解析Excel文件到List xls类型
     *
     * @param is         要解析的流如果是servlet的输入流不需要手动关闭，其他自己手动创建的流需要自己关掉，在此方法中没有做关闭处理
     * @param sheetIndex 标签的下标
     * @param startRow   真实数据的开始行 从0开始数
     * @param startCol   真实数据的开始列 从0开始数
     * @return List<Map>  map的key就是列数  从0开始计数
     */
    public static List<Map<Integer, String>> analysis(InputStream is, int sheetIndex, int startRow, int startCol) throws Exception {

        Workbook wb = null;

        List<Map<Integer, String>> dataMapList = new ArrayList<>();
        try {
            wb = WorkbookFactory.create(is);
            Sheet sheet = wb.getSheetAt(sheetIndex);
            if (sheet == null) {
                return null;
            }
            for (int i = startRow; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    continue;
                }
                LinkedHashMap<Integer, String> dataMap = new LinkedHashMap();
                for (int j = startCol; j < row.getLastCellNum(); j++) {
                    Cell cell = row.getCell(j);
                    String cellValue = getCellValue(cell);
                    dataMap.put(j, cellValue);

                }
                dataMapList.add(dataMap);
            }
        } catch (Exception e) {
            throw new ExcelException("Parse excel error", e);
        } finally {
            if (wb != null) {
                wb.close();
            }
        }
        return dataMapList;
    }

    private static String getCellValue(Cell cell) {
        if (cell == null || "".equals(cell)) {
            return "";
        }
        String cellValue = "";
        DecimalFormat df = new DecimalFormat("#");
        if (CellType.STRING.equals(cell.getCellType())) {
            cellValue = cell.getRichStringCellValue().getString().trim();
        } else if (CellType.NUMERIC.equals(cell.getCellType())) {
            cellValue = df.format(cell.getNumericCellValue());
        } else if (CellType.BOOLEAN.equals(cell.getCellType())) {
            cellValue = String.valueOf(cell.getBooleanCellValue()).trim();
        } else if (CellType.FORMULA.equals(cell.getCellType())) {
            cellValue = cell.getCellFormula();
        } else if (CellType.BLANK.equals(cell.getCellType())) {
            cellValue = "";
        } else if (CellType.ERROR.equals(cell.getCellType())) {
            cellValue = "";
        } else {
            cellValue = "";
        }
        return cellValue;
    }
}
